package com.comba.nutz.boot.starter.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.comba.nutz.boot.starter.datasource.DataSourceContextHolder;
import com.comba.nutz.boot.starter.datasource.DynamicDataSource;
import com.comba.nutz.boot.starter.properties.DynamicDataSourceProperties;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lizhiyong1
 */
@Configuration
@ConditionalOnClass(DynamicDataSource.class)
@AutoConfigureBefore(DruidDataSourceAutoConfigure.class)
@EnableConfigurationProperties({DynamicDataSourceProperties.class, DruidStatProperties.class, DataSourceProperties.class})
public class DynamicDataSourceAutoConfigure extends DruidDataSourceAutoConfigure {

    @Autowired
    private DynamicDataSourceProperties dynamicDataSourceProperties;

    @Autowired
    private Environment environment;

    private ConversionService conversionService = new DefaultConversionService();
    private PropertyValues dataSourcePropertyValues;

    @Override
    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(getDataSource(dynamicDataSourceProperties.getDefaultDataSource()));
        DataSourceContextHolder.addDataSourceType("default");
        dynamicDataSource.setTargetDataSources(getTargetDataSources(dynamicDataSourceProperties.getTargetDataSources()));
        return dynamicDataSource;
    }

    private Map<Object, Object> getTargetDataSources(Map<String, DataSourceProperties> customDataSources) {
        Map<Object, Object> targetDataSources = new HashMap<>(16);
        if (customDataSources != null) {

            for (Map.Entry<String, DataSourceProperties> entry : customDataSources.entrySet()) {
                String key = entry.getKey();
                DataSource dataSource = getDataSource(entry.getValue());
                targetDataSources.put(key, dataSource);
                DataSourceContextHolder.addDataSourceType(key);
            }
        }
        return targetDataSources;
    }

    private DataSource getDataSource(DataSourceProperties dataSourceProperties) {
        DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
        dataBinder(druidDataSource, environment);
        if (dataSourceProperties != null) {
            if (dataSourceProperties.determineUsername() != null) {
                druidDataSource.setUsername(dataSourceProperties.determineUsername());
            }
            if (dataSourceProperties.determinePassword() != null) {
                druidDataSource.setPassword(dataSourceProperties.determinePassword());
            }
            if (dataSourceProperties.determineUrl() != null) {
                druidDataSource.setUrl(dataSourceProperties.determineUrl());
            }
            if (dataSourceProperties.determineDriverClassName() != null) {
                druidDataSource.setDriverClassName(dataSourceProperties.determineDriverClassName());
            }
        }
        return druidDataSource;
    }

    private void dataBinder(DataSource dataSource, Environment environment) {
        RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
        dataBinder.setConversionService(conversionService);
        // false
        dataBinder.setIgnoreNestedProperties(false);
        // false
        dataBinder.setIgnoreInvalidFields(false);
        // true
        dataBinder.setIgnoreUnknownFields(true);

        if (dataSourcePropertyValues == null) {
            Map<String, Object> values = new RelaxedPropertyResolver(environment, "spring.datasource.druid")
                    .getSubProperties(".");
            dataSourcePropertyValues = new MutablePropertyValues(values);
        }

        dataBinder.bind(dataSourcePropertyValues);
    }
}
